using System;
using System.Runtime.Serialization;

namespace Ns.Data.Entity
{    
    /// <summary>
    /// Represents a type supporting change tracking, change notification, generic identifier and generic auditing of creation information.
    /// </summary>
    /// <typeparam name="TId">The type of the <see cref="IdEntity&lt;TId&gt;.Id"/> property.</typeparam>
    /// <typeparam name="TCreatedBy">The type of the <see cref="CreatedBy"/> property.</typeparam>
    /// <typeparam name="TCreatedOn">The type of the <see cref="CreatedOn"/> property.</typeparam>
    /// <remarks>Storage members are marked as protected in order to enable mapping with LinqToSql.</remarks>
#if !SILVERLIGHT
    [Serializable]
#endif
    [DataContract(Name="CreateEntityOf{0}")]
    public abstract class CreateEntity<TId, TCreatedBy, TCreatedOn> 
        : PersistEntity
        , IIdEntity<TId>
        , ICreateEntity<TCreatedBy, TCreatedOn>
    {
        #region IIdEntity<TId> Members

        /// <ignore/>
        protected TId _id;

        /// <summary>
        /// Gets or sets the entity identifier.
        /// </summary>
        /// <value>The entity Id.</value>
        [DataMember(EmitDefaultValue = false)]
        public virtual TId Id
        {
            get { return _id; }
            set { _id = value; }
        }
        #endregion

        #region ICreateEntity Members

        /// <ignore/>
        protected TCreatedBy _createdBy;

        /// <summary>
        /// Gets or sets the user who has created the current entity.
        /// </summary>
        /// <value>The creator.</value>
        [DataMember(EmitDefaultValue = false)]
        public virtual TCreatedBy CreatedBy
        {
            get { return _createdBy; }
            set { _createdBy = value; }
        }

        /// <ignore/>
        protected TCreatedOn _createdOn;

        /// <summary>
        /// Gets or sets the creation date and time of the current entity.
        /// </summary>
        /// <value>The creation date and time.</value>
        [DataMember(EmitDefaultValue = false)]
        public virtual TCreatedOn CreatedOn
        {
            get { return _createdOn; }
            set { _createdOn = value; }
        }

        #endregion
            
        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="CreateEntity&lt;TId,TCreatedBy,TCreatedOn&gt;"/> class
        /// with the <see cref="PersistEntity.CurrentPersistChange"/> 
        /// and according to the <see cref="PersistEntity.CurrentChangeTracking"/>.
        /// </summary>
        public CreateEntity()
            : this(CurrentPersistChange
            ?? ((CurrentChangeTracking == ChangeTracking.Enabled)
                ? PersistChange.Insert
                : PersistChange.None)
            , CurrentChangeTracking)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CreateEntity&lt;TId,TCreatedBy,TCreatedOn&gt;"/> class 
        /// with the <see cref="PersistEntity.CurrentPersistChange"/> 
        /// and according to the specified <see cref="ChangeTracking"/>.
        /// </summary>
        /// <param name="changeTracking">The change tracking.</param>
        public CreateEntity(ChangeTracking changeTracking)
            : this(CurrentPersistChange
            ?? ((changeTracking == ChangeTracking.Enabled)
                ? PersistChange.Insert
                : PersistChange.None)
            , changeTracking)
        { 
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CreateEntity&lt;TId,TCreatedBy,TCreatedOn&gt;"/> class 
        /// with the specified <see cref="PersistChange"/> 
        /// and according to the <see cref="PersistEntity.CurrentChangeTracking"/>.
        /// </summary>
        /// <param name="persistChange">The persist change.</param>
        public CreateEntity(PersistChange persistChange)
            : this(persistChange, CurrentChangeTracking)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CreateEntity&lt;TId,TCreatedBy,TCreatedOn&gt;"/> class 
        /// with the specified <see cref="PersistChange"/> 
        /// and according to the specified <see cref="ChangeTracking"/>.
        /// </summary>
        /// <param name="persistChange">The persist change.</param>
        /// <param name="changeTracking">The change tracking.</param>
        public CreateEntity(PersistChange persistChange, ChangeTracking changeTracking)
            : base(persistChange)
        {
            if (changeTracking == ChangeTracking.Enabled)
            {
                if (ChangingId != null)
                {
                    var args = new ChangingEventArgs<TId>();
                    ChangingId(this, args);
                    _id = args.Result;
                }

                if (ChangingCreatedBy != null)
                {
                    var args = new ChangingEventArgs<TCreatedBy>();
                    ChangingCreatedBy(this, args);
                    _createdBy = args.Result;
                }

                if (ChangingCreatedOn != null)
                {
                    var args = new ChangingEventArgs<TCreatedOn>();
                    ChangingCreatedOn(this, args);
                    _createdOn = args.Result;
                }
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CreateEntity&lt;TId,TCreatedBy,TCreatedOn&gt;"/> class
        /// copied from the specified <paramref name="entity"/>.
        /// </summary>
        /// <param name="entity">The entity.</param>
        /// <remarks>This constructor ignores any <see cref="ChangeTrackingScope"/> and <see cref="PersistChangeScope"/>.</remarks>
        /// <exception cref="ArgumentNullException"><paramref name="entity"/> is null.</exception>
        protected CreateEntity(CreateEntity<TId, TCreatedBy, TCreatedOn> entity)
            : base(entity)
        {
            this._createdBy = entity._createdBy;
            this._createdOn = entity._createdOn;
            this._id = entity._id;
        }

        #endregion        

        #region Overriden Members

        /// <summary>
        /// Converts the value of the current <see cref="CreateEntity&lt;TId,TCreatedBy,TCreatedOn&gt;"/> object to its equivalent string representation. 
        /// </summary>
        /// <returns>A string representation of the value of the current <see cref="CreateEntity&lt;TId,TCreatedBy,TCreatedOn&gt;"/> object.</returns>
        public override string ToString()
        {
            return string.Format("{0}, Id = {1}, CreatedBy = {2}, CreatedOn = {3}"
                , base.ToString()
                , Id
                , CreatedBy
                , CreatedOn);
        }

        #endregion        

        #region Events

        /// <summary>
        /// Clears handlers of the <see cref="ChangingId"/>, 
        /// <see cref="ChangingCreatedBy"/>, <see cref="ChangingCreatedOn"/> events.
        /// </summary>
        public static void ClearEventHandlers()
        {
            ChangingId = null;
            ChangingCreatedBy = null;
            ChangingCreatedOn = null;
        }
        /// <summary>
        /// Occurs when the <see cref="Id"/> gonna change.
        /// </summary>
        /// <remarks>This event enables centralization of generation.</remarks>
        public static event EventHandler<ChangingEventArgs<TId>> ChangingId;

        /// <summary>
        /// Occurs when the <see cref="CreatedBy"/> gonna change.
        /// </summary>
        /// <remarks>This event enables centralization of generation.</remarks>
        public static event EventHandler<ChangingEventArgs<TCreatedBy>> ChangingCreatedBy;

        /// <summary>
        /// Occurs when the <see cref="CreatedOn"/> gonna change.
        /// </summary>
        /// <remarks>This event enables centralization of generation.</remarks>
        public static event EventHandler<ChangingEventArgs<TCreatedOn>> ChangingCreatedOn;

        #endregion        
    }
}